// 2013, Arndt Teinert
// Very simple command line playlist player based on VLC

#include "stdafx.h"

#include <vlc/libvlc.h>
#include <vlc/libvlc_media.h>
#include <vlc/libvlc_media_player.h>

#include <vlc/libvlc_events.h>

#include <iostream>

#include <pugixml.hpp>

int seconds = 4;
int waitForEnter = 0;

typedef void(*cmd_line_opt_cb)( _TCHAR* [], int, int*);

libvlc_media_t* media			= NULL;
libvlc_media_player_t* player	= NULL;
libvlc_instance_t* instance		= NULL;
libvlc_event_manager_t *events	= NULL;

HANDLE hStoppedEvent;
HANDLE hStoppedListEvent;

void cmd_set_pause( _TCHAR* argv[], int argc, int* index) 
{
	(*index)++;
	if (*index < argc) {
		_stscanf_s(argv[*index],_T("%d"),&seconds);
	}
}

void cmd_wait_for_enter( _TCHAR* argv[], int argc, int* index) 
{
	(*index)++;
	waitForEnter = 1;
}

typedef struct cmd_option {
	char name[64];
	cmd_line_opt_cb callback;
} cmd_option_t;

cmd_option_t cmd_options[] = 
{
	{"--pause", cmd_set_pause},
	{"--wait", cmd_wait_for_enter}
};

void onStopped(const struct libvlc_event_t *pEvent, void *) 
{
	SetEvent(hStoppedEvent);
}

void onError(const struct libvlc_event_t *pEvent, void *) 
{
	printf("Error during play: %s\n",libvlc_errmsg());
	SetEvent(hStoppedEvent);
}

// Plays the given file and waits for the player to stop or for a player error
void PlayFile(const char* pFilePath, const char* name, const char* creator, const char* album) 
{
	media = libvlc_media_new_location(instance, pFilePath);
	libvlc_media_parse(media);
	libvlc_time_t length = libvlc_media_get_duration(media);

	libvlc_media_player_set_media(player, media);
	libvlc_media_release(media);
	media = NULL;

	int rc = libvlc_media_player_play(player);
	std::string str = pFilePath;
	if (name) {
		str = name;
		if (creator) {
			str = str + " - " + creator;
		}
		if (album) {
			str = str + " - " + album;
		}
	}

	printf("Playing: %s (%02d:%02d)\n", str.c_str(), (int)length / 60000, (int)(length % 60000) / 1000);
	// libvlc_time_t length = libvlc_media_player_get_length(player);
	while (WaitForSingleObject(hStoppedEvent, 100) == WAIT_TIMEOUT) {
		libvlc_time_t currentTime = libvlc_media_player_get_time(player);
		libvlc_time_t diff = (length - currentTime);
		printf("Time left: %02d:%02d\r", (int)diff / 60000, (int)(diff % 60000) / 1000);
	}
	printf("                            \r");
	libvlc_media_player_set_media(player, NULL);
}

// Reads the VLC playlist and plays every track with a pause of n secondes between each track
void PlayFiles(const TCHAR* path) 
{
	pugi::xml_document doc;
	pugi::xml_parse_result result = doc.load_file(path);
	if (result) {
		pugi::xml_node playList = doc.child("playlist");
		pugi::xml_node trackList = playList.child("trackList");
		for (pugi::xml_node track = trackList.child("track"); track; track = track.next_sibling("track")) {
			const pugi::char_t *pName = NULL;
			const pugi::char_t *pCreator = NULL;
			const pugi::char_t *pAlbum = NULL;

			if (track.child("title").type() != pugi::node_null) {
				pName = track.child_value("title");
			}
			if (track.child("title").type() != pugi::node_null) {
				pCreator = track.child_value("creator");
			}
			if (track.child("title").type() != pugi::node_null) {
				pAlbum = track.child_value("album");
			}
			PlayFile(track.child_value("location"), pName, pCreator, pAlbum);
			_tprintf(_T("Waiting for %d seconds...\r"),seconds);
			Sleep(seconds * 1000);
			_tprintf(_T("                                  \r"),seconds);
		}
	} else {
		_tprintf(_T("XML [%s] parsed with errors!\n"), path);
		printf("Error description: %s\n", result.description());
		printf("Error offset: %d\n", result.offset);
	}
}

// the play thread
DWORD WINAPI PlayFunction(LPVOID data) 
{
	PlayFiles((TCHAR*)data);

	// let main exit
	SetEvent(hStoppedListEvent);
	return 0;
}

// process the command line options
void ProcessCmdLine(int argc, _TCHAR* argv[]) 
{
	int index = 2;
	while(index < argc) {
		for (size_t ii = 0; ii < sizeof(cmd_options) / sizeof(cmd_option); ii++) {
			if (strcmp(cmd_options[ii].name, argv[index]) == 0) {
				cmd_options[ii].callback(argv, argc, &index);
			}
		}
		index++;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	if (argc >= 2) {
		_tprintf(_T("Reading playlist: %s"),argv[1]);

		ProcessCmdLine(argc, argv);

		hStoppedEvent = CreateEvent(NULL,FALSE,FALSE,_T("STOPPED"));
		hStoppedListEvent  = CreateEvent(NULL,FALSE,FALSE,_T("STOPPEDLIST"));

		instance = libvlc_new(0, NULL);
		player = libvlc_media_player_new(instance);
		events = libvlc_media_player_event_manager(player);
		libvlc_event_attach(events, libvlc_MediaPlayerEndReached, onStopped, NULL);
		libvlc_event_attach(events, libvlc_MediaPlayerEncounteredError, onError, NULL);
	 
		libvlc_audio_set_volume(player,100);
	
		_tprintf(_T(" -> Done.\n"));
		if (waitForEnter) {
			printf("Press enter to start!");
			char c = getc(stdin);
		}

		HANDLE hThread = CreateThread(NULL,0,PlayFunction,(LPVOID)argv[1],0,NULL);
		SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);

		WaitForSingleObject(hStoppedListEvent,INFINITE);

		_tprintf(_T("Finished.\n"));
		libvlc_media_player_release(player);
		player = NULL;
		events = NULL;

		libvlc_release(instance);
		instance = NULL;
	}
	return 0;
}

